package sg.edu.nus.iss.jass.datastore.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import sg.edu.nus.iss.jass.datastore.BaseDTO;
import sg.edu.nus.iss.jass.datastore.IDataMapper;
import sg.edu.nus.iss.jass.datastore.util.PropertiesUtil;


/**
 * <p>An implementation of IDataMapper. Refer to {@link IDataMapper} </p>
 * 
 * @author WANG DAYONG
 *
 */
public class DataMapper implements IDataMapper {

	private static final String ID = "id";

	@Override
	public List<? extends BaseDTO> mapProperties2Object(Properties prop) {

		if (prop.isEmpty()) {
			return null;
		}

		Map<String, Map> container = new HashMap<String, Map>();

		for (Map.Entry<Object, Object> entry : prop.entrySet()) {
			String key = entry.getKey().toString();
			int index = key.lastIndexOf(SEPARATOR_CHAR);
			if(index < 0) continue;
			String id = key.substring(index+1);
			if (container.containsKey(id)) {
				container.get(id).put(key.substring(0, index), entry.getValue());
			} else {
				Map bean = new HashMap();
				bean.put(ID, id);
				bean.put(key.substring(0, index), entry.getValue());
				container.put(id, bean);
			}
		}

		List result = new ArrayList();

		for (Map.Entry<String, Map> entry : container.entrySet()) {
			result.add(this.map2entity(entry.getValue(), prop.getProperty(TABLE_NAME)));
		}

		return result;
	}

	@Override
	public Properties mapObject2Properties(List<? extends BaseDTO> list) {

		Properties prop = new Properties();
		String tableName = list.get(0).getClass().getName();
		String id = "";
		for (BaseDTO baseEnt : list) {
			Map<String, String> map = null;
			if (baseEnt instanceof Map) {
				map = (Map) baseEnt;
			} else {
				id = baseEnt.getId();
				map =this.entity2map(baseEnt);
			}
			for (Map.Entry<String, String> entity : map.entrySet()) {
				// TODO:if the value of map is not string?
				String key = entity.getKey().toString();
				if(key.equals("class")) {
					continue;
				};	
				key = key.concat(SEPARATOR_CHAR).concat(id);
				String value = entity.getValue()==null ? "" : entity.getValue().toString();
				prop.setProperty(key, value);
			}
		}
		prop.setProperty(TABLE_NAME, tableName);
		return prop;
	}
	
	/**
	 *  <p>Get key/value pairs from given object</p>
	 *  Any error is ignored during transform.
	 *  
	 * @param <T> child class of BaseDTO
	 * @param obj The source object is desired to transform to key/value pairs. 
	 * @return map The key/value pairs stored <code>HashMap</code>.
	 */
	private <T extends BaseDTO> Map<String, String> entity2map(T obj) {

		if(obj == null)
			return null;
		Map<String, String> map = null;
		try {
			map = PropertiesUtil.describeBean(obj);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return map;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	/**
	 * <p>To populate key/value pairs to object of given class name</p>
	 * 	
	 * @param <T> child class of BaseDTO
	 * @param map The container carrying the key/value pairs
	 * @param clazz The target class name ,default constructor is required in this class.  
	 * @return An instance of given class name with contents from given map. 
	 * 			Return <code>null</code>, if the class is not found
	 */
	private <T extends BaseDTO> T map2entity(Map<String, String> map, String clazz) {

		Class beanClass = null;
		Object beanInstance = null;
		try {
			beanClass = Class.forName(clazz);
			beanInstance = beanClass.newInstance();
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		if(beanInstance == null)
			return null;
		
		try {
			if(beanInstance instanceof Map) {
				((Map)beanInstance).putAll(map);
			}else{
				PropertiesUtil.populateBean(beanInstance, map);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return (T)beanInstance;
	}

}
